﻿namespace MicroCloud.Utils
{
    /// <summary>
    /// 基类型 <see cref="Object"/> 扩展辅助操作类
    /// </summary>
    public static class ObjectExtensions
    {        
        #region "方法"
        #region "把对象类型转换为指定类型"
        /// <summary>
        /// 把对象类型转换为指定类型
        /// </summary>
        /// <param name="value">要转化的源对象</param>
        /// <param name="conversionType">动态类型</param>
        /// <returns></returns>
        public static object CastTo(this object value, Type conversionType)
        {
            if (value == null)
            {
                return null;
            }
            if (conversionType.IsNullableType())
            {
                conversionType = conversionType.GetUnNullableType();
            }
            if (conversionType.IsEnum)
            {
                return Enum.Parse(conversionType, value.ToString());
            }
            if (conversionType == typeof(Guid))
            {
                return Guid.Parse(value.ToString());
            }
            if (conversionType == typeof(DateTime))
            {
                return DateTime.Parse(value.ToString());
            }
            if (conversionType == typeof(IPAddress))
            {
                return IPAddress.Parse(value.ToString());
            }
            return Convert.ChangeType(value, conversionType);
        }
        #endregion
        #region "把对象类型转换为指定类型"
        /// <summary>
        /// 把对象类型转换为指定类型
        /// </summary>
        /// <param name="value">要转化的源对象</param>
        /// <param name="conversionType">动态类型</param>
        /// <param name="defaultValue">转化失败返回的指定默认值</param>
        /// <returns></returns>
        public static object CastTo(this object value, Type conversionType, object defaultValue)
        {
            try
            {
                return CastTo(value, conversionType);
            }
            catch (System.Exception)
            {
                return defaultValue;
            }
        }
        #endregion
        #region "把对象类型转化为指定类型"
        /// <summary>
        /// 把对象类型转化为指定类型
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="value">要转化的源对象</param>
        /// <returns>转化后的指定类型的对象，转化失败引发异常。</returns>
        public static T CastTo<T>(this object value)
        {
            if (value == null && default(T) == null)
            {
                return default;
            }
            if (value?.GetType() == typeof(T))
            {
                return (T)value;
            }
            object result = CastTo(value, typeof(T));
            result ??= default(T);
            return (T)result;
        }
        #endregion
        #region "把对象类型转化为指定类型，转化失败时返回指定的默认值"
        /// <summary>
        /// 把对象类型转化为指定类型，转化失败时返回指定的默认值
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="value">要转化的源对象</param>
        /// <param name="defaultValue">转化失败返回的指定默认值</param>
        /// <returns>转化后的指定类型对象，转化失败时返回指定的默认值。</returns>
        public static T CastTo<T>(this object value, T defaultValue)
        {
            try
            {
                return CastTo<T>(value);
            }
            catch (System.Exception)
            {
                return defaultValue;
            }
        }
        #endregion

        #region "判断当前值是否介于指定范围内"
        /// <summary>
        /// 判断当前值是否介于指定范围内
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="value">动态类型对象</param>
        /// <param name="start">范围起点</param>
        /// <param name="end">范围终点</param>
        /// <param name="leftEqual">是否可等于上限(默认等于)</param>
        /// <param name="rightEqual">是否可等于下限(默认等于)</param>
        /// <returns>是否介于</returns>
        public static bool IsBetween<T>(this IComparable<T> value, T start, T end, bool leftEqual = true, bool rightEqual = true) where T : IComparable
        {
            bool flag = leftEqual ? value.CompareTo(start) >= 0 : value.CompareTo(start) > 0;
            return flag && (rightEqual ? value.CompareTo(end) <= 0 : value.CompareTo(end) < 0);
        }
        #endregion
        #region "判断当前值是否介于指定范围内"
        /// <summary>
        /// 判断当前值是否介于指定范围内
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="value">动态类型对象</param>
        /// <param name="min">范围小值</param>
        /// <param name="max">范围大值</param>
        /// <param name="minEqual">是否可等于小值(默认等于)</param>
        /// <param name="maxEqual">是否可等于大值(默认等于)</param>
        public static bool IsInRange<T>(this IComparable<T> value, T min, T max, bool minEqual = true, bool maxEqual = true) where T : IComparable
        {
            bool flag = minEqual ? value.CompareTo(min) >= 0 : value.CompareTo(min) > 0;
            return flag && (maxEqual ? value.CompareTo(max) <= 0 : value.CompareTo(max) < 0);
        }
        #endregion

        #region "是否存在于"
        /// <summary>
        /// 是否存在于
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="value">动态类型对象</param>
        /// <param name="source">是否存在于的对象数组</param>
        /// <returns></returns>
        public static bool IsIn<T>(this T value, params T[] source)
        {
            return source.Contains(value);
        }
        #endregion

        #region "将对象[主要是匿名对象]转换为dynamic"
        /// <summary>
        /// 将对象[主要是匿名对象]转换为dynamic
        /// </summary>
        /// <param name="value">要转化的源对象</param>
        /// <returns>dynamic对象</returns>
        public static dynamic ToDynamic(this object value)
        {
            IDictionary<string, object> expando = new ExpandoObject();
            Type type = value.GetType();
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(type);
            foreach (PropertyDescriptor property in properties)
            {
                var val = property.GetValue(value);
                if (property.PropertyType.FullName != null && property.PropertyType.FullName.StartsWith("<>f__AnonymousType"))
                {
                    dynamic dval = val.ToDynamic();
                    expando.Add(property.Name, dval);
                }
                else
                {
                    expando.Add(property.Name, val);
                }
            }
            return (ExpandoObject)expando;
        }
        #endregion
     
        #endregion

    }

}
